"""
time: 2024/10/28
title: SentryServer
message:
哨兵服务
"""
import json
import time
import socket
import datetime
from Tool.Record import Record
from Tool.Internet import Internet


class SentryServer:
    def __init__(self):
        self.server_url = "http://127.0.0.1:10801"
        self.net = Internet(self.server_url)
        self.cyclone_net = Internet("http://localhost:8700")
        self.sid = "bcdefghijklmnopqrstuvwxyz{23456789:101"
        self.robot_url = "http://localhost:8700"
        self.hear = {'Content-Type': 'application/json'}
        self.robot_id = None
        self.robot_name = socket.gethostname()
        self.robot_ip = socket.gethostbyname(self.robot_name)
        self.log = Record(name='哨兵', console=True)

    @staticmethod
    def time_format(time_str):
        format_time = datetime.datetime.strptime(time_str, "%Y%m%d-%H%M%S")
        format_time_str = format_time.strftime("%Y-%m-%d %H:%M:%S")
        return format_time_str

    def get_robot_id(self):
        """
        获取本地ROBOT_ID
        :return:
        """
        robot_info_list = self.net.internet_post('/WorkingCondition', data={'sid': self.sid, 'ip': self.robot_ip})
        if robot_info_list['status'] == 0:
            if len(robot_info_list['data']) == 0:
                new_robot = {'sid': self.sid, 'ip': self.robot_ip, 'name': self.robot_name, 'status': '空闲中',
                             'health': '健康', 'remark': '无', 'username': self.robot_name}
                new_robot = self.net.internet_post('/robot/info/new', data=new_robot)
                return new_robot['robot_id']
            else:
                return robot_info_list['data'][0]['robot_id']
        else:
            return None

    def get_flow_info(self):
        """
        获取流程信息
        :return:
        """
        # TODO 轮询时第一个启用 本地机器人ID
        if self.robot_id is None:
            self.robot_id = self.get_robot_id()

        # 获取数据库中所有流程信息
        server_flow_info = self.net.internet_post('/Quest_list', data={'sid': self.sid, 'robot_id': self.robot_id})

        # 获取机器人流程信息
        robot_flow_info = self.cyclone_net.internet_get(url='/mrpa/list')

        # 机器人流程列表
        robot_flow_list = robot_flow_info['mrpa_file_list']
        # 服务器流程列表
        server_flow_list = [cell['mrpa_id'] for cell in server_flow_info['data']]
        # 获取服务器未同步流程
        for cell in robot_flow_list:
            if cell['mrpa_id'] not in server_flow_list:
                # 添加流程 /flow/info/add
                add_json = {'sid': self.sid, 'robot_id': self.robot_id, 'mrpa_id': cell['mrpa_id'],
                            'flow_name': cell['file_name'].replace('.mrpax', ''), 'file_path': cell['file_path'],
                            'file_md5': cell['file_md5'], 'grouping': "", 'status': 2}
                add_info = self.net.internet_post('/flow/info/add', data=add_json)
                self.log.info(f"流程新增, 新增返回信息: {json.dumps(add_info)}")

    def update_flow_status(self, history_id, flow_id, status, workflow_id, stop_time=None, remarks='哨兵唤醒',
                           button=None):
        """
        更新流程状态信息
        :param history_id:
        :param flow_id:
        :param status:
        :param workflow_id:
        :param stop_time:
        :param remarks:
        :param button: 按钮状态
        :return:
        """
        stop = f", stop_time='{self.time_format(stop_time)}'" if stop_time is not None else ''
        sql = f"""UPDATE flow_history SET status='{status}', 
        message='哨兵处理:{remarks}', grouping='{status}', 
        remake='{remarks}',work_flow_id='{workflow_id}' {stop}
        WHERE flow_id='{flow_id}' AND `id`='{history_id}'"""
        data = {'sid': self.sid, 'sql_lang': sql}
        self.net.internet_post(url='/rpa/sql/run', data=data)
        if button is not None:
            sql = f"UPDATE flow_info SET status = '{button}' WHERE id = '{flow_id}'"
            data = {'sid': self.sid, 'sql_lang': sql}
            self.net.internet_post(url='/rpa/sql/run', data=data)

    def update_robot_status(self, status):
        """
        更新机器人工作状态
        :param status:
        :return:
        """
        if self.robot_id is None:
            self.robot_id = self.get_robot_id()
        data = {'sid': self.sid, 'robot_id': self.robot_id, 'status': status}
        robot_set = self.net.internet_post(url='/robot/info/set', data=data)
        print(robot_set)

    def get_executed_flow(self):
        """
        获取待执行流程
        :return:
        """
        url_data = {'sid': self.sid, 'robot_id': self.robot_id, 'group': '待执行'}
        treat_execute_flow = self.net.internet_post('/Execution_history', data=url_data)
        print(f"待执行流程:{treat_execute_flow['count']}条")
        for flow in treat_execute_flow['data']:
            self.log.info(f"开始执行{flow['flow_name']}流程")
            url = f"/executor/run/async/{flow['mrpa_id']}"
            start_type = self.cyclone_net.internet_post(url=url, data={'mrpa_id': flow['flow_id']})
            if start_type['error_code'] == 0:
                # 更新流程状态为运行中
                self.update_flow_status(history_id=flow['id'], flow_id=flow['flow_id'], status='运行中',
                                        workflow_id=start_type['workflow_id'], button='0')
                self.log.info(f"流程[{flow['flow_name']}]启动成功")
                # 更新机器人状态为工作中 - /robot/info/set
                self.update_robot_status(status='工作中')
            else:
                # 更新流程状态为启动失败
                self.update_flow_status(history_id=flow['id'], flow_id=flow['flow_id'], status='唤醒失败',
                                        workflow_id="ERROR CODE", button='2')
                self.log.error(f"流程[{flow['flow_name']}]启动失败")
                self.update_robot_status(status='空闲中')

    def get_stop_flow(self):
        """
        获取需要停止流程
        :return:
        """
        url_data = {'sid': self.sid, 'robot_id': self.robot_id, 'group': '待停止'}
        stop_execute_flow = self.net.internet_post('/Execution_history', data=url_data)
        print(f"待停止流程:{stop_execute_flow['count']}条")
        for flow in stop_execute_flow['data']:
            stop_tim = datetime.datetime.now().strftime('%Y%m%d-%H%M%S')
            self.log.info(f"停止流程:{flow['flow_name']}")
            msg = "正常停止流程"
            if flow['work_flow_id'] is not None:
                stop_url = f"/executor/stop/{flow['work_flow_id']}"
                self.cyclone_net.internet_post(url=stop_url)
                msg = "获取到空的work flow id 异常停止"
            self.update_flow_status(history_id=flow['id'], flow_id=flow['flow_id'], status='停止执行',
                                    workflow_id=flow['work_flow_id'], stop_time=stop_tim, remarks=msg, button='1')
            self.update_robot_status(status='空闲中')

    def get_flow_state(self):
        """
        获取流程状态
        :return:
        """
        if self.robot_id is None:
            self.robot_id = self.get_robot_id()
        # 获取所有运行中流程
        url_data = {'sid': self.sid, 'robot_id': self.robot_id, 'group': '运行中'}
        flow_state = self.net.internet_post('/Execution_history', data=url_data)
        print(f"运行中流程:{flow_state['count']}条")
        if flow_state['count'] == 0:
            url_data = {'sid': self.sid, 'robot_id': self.robot_id, 'group': '停止执行'}
            flow_state = self.net.internet_post('/Execution_history', data=url_data)
            print(f"手动停止流程:{flow_state['count']}条")
        for flow in flow_state['data']:
            # 获取流程状态
            cyclone_flow = self.cyclone_net.internet_get(url=f"/executor/history/{flow['work_flow_id']}")
            if cyclone_flow['error_code'] == 0:
                history = cyclone_flow['history']
                if 'output' in history.keys():
                    # 更新流程状态 -- 失败
                    if 'err' in history['result'].keys():
                        self.update_flow_status(history_id=flow['id'], flow_id=flow['flow_id'], status='执行失败',
                                                workflow_id=flow['work_flow_id'], stop_time=history['endTime'],
                                                remarks=json.dumps(history['result']), button='1')
                    else:
                        # 更新流程状态 -- 成功
                        self.update_flow_status(history_id=flow['id'], flow_id=flow['flow_id'], status='执行成功',
                                                workflow_id=flow['work_flow_id'], stop_time=history['endTime'],
                                                remarks="SUCCESS", button='1')
                    self.update_robot_status(status='空闲中')
                else:
                    # 运行中 不进行更新
                    print(f"流程【{history['mrpaFileName']}】运行中")

            else:
                print("Cyclone信息获取失败")
                self.update_flow_status(history_id=flow['id'], flow_id=flow['flow_id'], status='执行失败',
                                        workflow_id=flow['work_flow_id'], stop_time=None,
                                        remarks='正常结束流程', button='1')


if __name__ == '__main__':
    cls = SentryServer()
    cls.robot_id = cls.get_robot_id()
    while True:
        cls.get_flow_info()
        cls.get_executed_flow()
        cls.get_flow_state()
        cls.get_stop_flow()
        time.sleep(2)
